home *** CD-ROM | disk | FTP | other *** search
/ PC Format (PL) 2008 February / PC_Format_022008.iso / Internet / Mozilla Thunderbird wtyczki / lightning-0.7-tb-win.xpi / js / calWcapSession.js < prev    next >
Encoding:
JavaScript  |  2007-09-23  |  52.2 KB  |  1,265 lines

  1. /* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Sun Microsystems code.
  16.  *
  17.  * The Initial Developer of the Original Code is
  18.  * Sun Microsystems, Inc.
  19.  * Portions created by the Initial Developer are Copyright (C) 2007
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *   Daniel Boelzle <daniel.boelzle@sun.com>
  24.  *   Philipp Kewisch <mozilla@kewis.ch>
  25.  *
  26.  * Alternatively, the contents of this file may be used under the terms of
  27.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  28.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29.  * in which case the provisions of the GPL or the LGPL are applicable instead
  30.  * of those above. If you wish to allow use of your version of this file only
  31.  * under the terms of either the GPL or the LGPL, and not to allow others to
  32.  * use your version of this file under the terms of the MPL, indicate your
  33.  * decision by deleting the provisions above and replace them with the notice
  34.  * and other provisions required by the GPL or the LGPL. If you do not delete
  35.  * the provisions above, a recipient may use your version of this file under
  36.  * the terms of any one of the MPL, the GPL or the LGPL.
  37.  *
  38.  * ***** END LICENSE BLOCK ***** */
  39.  
  40. var g_openSessions = {};
  41. function getWcapSessionFor(cal, uri) {
  42.     var contextId = getCalendarManager().getCalendarPref(cal, "shared_context");
  43.     if (!contextId) {
  44.         contextId = getUUID();
  45.     }
  46.     var session = g_openSessions[contextId];
  47.     if (!session) {
  48.         session = new calWcapSession(contextId, uri);
  49.         g_openSessions[contextId] = session;
  50.     }
  51.     if (!session.defaultCalendar && cal.isDefaultCalendar) {
  52.         session.defaultCalendar = cal;
  53.     }
  54.     return session;
  55. }
  56.  
  57. function calWcapSession(contextId, thatUri) {
  58.     this.wrappedJSObject = this;
  59.     this.m_contextId = contextId;
  60.     this.m_observers = [];
  61.     this.m_loginQueue = [];
  62.  
  63.     this.m_uri = thatUri.clone();
  64.     this.m_sessionUri = thatUri.clone();
  65.     this.m_sessionUri.userPass = "";
  66.     // sensible default for user id login:
  67.     var username = decodeURIComponent(thatUri.username);
  68.     if (username.length > 0)
  69.         this.credentials.userId = username;
  70.     log("new session", this);
  71.  
  72.     // listen for shutdown, being logged out:
  73.     var observerService = Components.classes["@mozilla.org/observer-service;1"]
  74.                                     .getService(Components.interfaces.nsIObserverService);
  75.     observerService.addObserver(this, "quit-application", false /* don't hold weakly */);
  76.     getCalendarManager().addObserver(this);
  77. }
  78. calWcapSession.prototype = {
  79.     m_ifaces: [ calIWcapSession,
  80.                 calIFreeBusyProvider,
  81.                 Components.interfaces.calICalendarManagerObserver,
  82.                 Components.interfaces.nsIInterfaceRequestor,
  83.                 Components.interfaces.nsIClassInfo,
  84.                 nsISupports ],
  85.     
  86.     // nsISupports:
  87.     QueryInterface: function calWcapSession_QueryInterface(iid) {
  88.         ensureIID(this.m_ifaces, iid); // throws
  89.         return this;
  90.     },
  91.     
  92.     // nsIClassInfo:
  93.     getInterfaces: function calWcapSession_getInterfaces(count)
  94.     {
  95.         count.value = this.m_ifaces.length;
  96.         return this.m_ifaces;
  97.     },
  98.     get classDescription() {
  99.         return calWcapCalendarModule.WcapSessionInfo.classDescription;
  100.     },
  101.     get contractID() {
  102.         return calWcapCalendarModule.WcapSessionInfo.contractID;
  103.     },
  104.     get classID() {
  105.         return calWcapCalendarModule.WcapSessionInfo.classID;
  106.     },
  107.     getHelperForLanguage:
  108.     function calWcapSession_getHelperForLanguage(language) { return null; },
  109.     implementationLanguage:
  110.     Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
  111.     flags: 0,
  112.     
  113.     // nsIInterfaceRequestor:
  114.     getInterface: function calWcapSession_getInterface(iid, instance) {
  115.         if (iid.equals(Components.interfaces.nsIAuthPrompt)) {
  116.             // use the window watcher service to get a nsIAuthPrompt impl
  117.             return getWindowWatcher().getNewAuthPrompter(null);
  118.         }
  119.         else if (iid.equals(Components.interfaces.nsIPrompt)) {
  120.             // use the window watcher service to get a nsIPrompt impl
  121.             return getWindowWatcher().getNewPrompter(null);
  122.         }
  123.         Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
  124.         return null;
  125.     },
  126.     
  127.     toString: function calWcapSession_toString(msg)
  128.     {
  129.         var str = ("context-id: " + this.m_contextId + ", uri: " + this.uri.spec);
  130.         if (this.credentials.userId) {
  131.             str += (", userId=" + this.credentials.userId);
  132.         }
  133.         if (!this.m_sessionId) {
  134.             str += (getIoService().offline ? ", offline" : ", not logged in");
  135.         }
  136.         return str;
  137.     },
  138.     notifyError: function calWcapSession_notifyError(err, suppressOnError)
  139.     {
  140.         if (getResultCode(err) == calIErrors.OPERATION_CANCELLED) {
  141.             return;
  142.         }
  143.         debugger;
  144.         var msg = logError(err, this);
  145.         if (!suppressOnError && this.defaultCalendar) {
  146.             // xxx todo: currently takes observer bag of default calendar (which is always present):
  147.             this.defaultCalendar.notifyObservers(
  148.                 "onError",
  149.                 err instanceof Components.interfaces.nsIException
  150.                 ? [err.result, err.message] : [isNaN(err) ? -1 : err, msg]);
  151.         }
  152.     },
  153.  
  154.     m_serverTimezones: null,
  155.     isSupportedTimezone: function calWcapSession_isSupportedTimezone(tzid)
  156.     {
  157.         if (!this.m_serverTimezones) {
  158.             throw new Components.Exception(
  159.                 "early run into getSupportedTimezones()!",
  160.                 Components.results.NS_ERROR_NOT_AVAILABLE);
  161.         }
  162.         return this.m_serverTimezones.some(
  163.             function someFunc(id) { return tzid == id; } );
  164.     },
  165.     
  166.     m_serverTimeDiff: null,
  167.     getServerTime: function calWcapSession_getServerTime(localTime)
  168.     {
  169.         if (this.m_serverTimeDiff === null) {
  170.             throw new Components.Exception(
  171.                 "early run into getServerTime()!",
  172.                 Components.results.NS_ERROR_NOT_AVAILABLE);
  173.         }
  174.         var ret = (localTime ? localTime.clone() : getTime());
  175.         ret.addDuration(this.m_serverTimeDiff);
  176.         return ret;
  177.     },
  178.     
  179.     m_sessionId: null,
  180.     m_loginQueue: null,
  181.     m_loginLock: false,
  182.     
  183.     getSessionId:
  184.     function calWcapSession_getSessionId(request, respFunc, timedOutSessionId)
  185.     {
  186.         if (getIoService().offline) {
  187.             log("in offline mode.", this);
  188.             respFunc(new Components.Exception(
  189.                          "The requested action could not be completed while the " +
  190.                          "networking library is in the offline state.",
  191.                          NS_ERROR_OFFLINE));
  192.             return;
  193.         }
  194.         
  195.         log("login queue lock: " + this.m_loginLock +
  196.             ", length: " + this.m_loginQueue.length, this);
  197.         
  198.         if (this.m_loginLock) {
  199.             this.m_loginQueue.push(respFunc);
  200.             log("login queue: " + this.m_loginQueue.length);
  201.         }
  202.         else {
  203.             if (this.m_sessionId && this.m_sessionId != timedOutSessionId) {
  204.                 respFunc(null, this.m_sessionId);
  205.                 return;
  206.             }
  207.             
  208.             this.m_loginLock = true;
  209.             log("locked login queue.", this);
  210.             this.m_sessionId = null; // invalidate for relogin
  211.             
  212.             if (timedOutSessionId) {
  213.                 log("reconnecting due to session timeout...", this);
  214.                 getFreeBusyService().removeProvider(this);
  215.             }
  216.             
  217.             var this_ = this;
  218.             this.getSessionId_(
  219.                 request,
  220.                 function getSessionId_resp(err, sessionId) {
  221.                     log("getSessionId_resp(): " + sessionId, this_);
  222.                     if (err) {
  223.                         this_.notifyError(err, request.suppressOnError);
  224.                     }
  225.                     else {
  226.                         this_.m_sessionId = sessionId;
  227.                         getFreeBusyService().addProvider(this_);
  228.                     }
  229.                     
  230.                     var queue = this_.m_loginQueue;
  231.                     this_.m_loginLock = false;
  232.                     this_.m_loginQueue = [];
  233.                     log("unlocked login queue.", this_);
  234.  
  235.                     function exec(func) {
  236.                         try {
  237.                             func(err, sessionId);
  238.                         }
  239.                         catch (exc) {
  240.                             this_.notifyError(exc);
  241.                         }
  242.                     }
  243.                     // answer first request:
  244.                     exec(respFunc);
  245.                     // and any remaining:
  246.                     queue.forEach(exec);
  247.                 });
  248.         }
  249.     },
  250.     
  251.     getSessionId_: function calWcapSession_getSessionId_(request, respFunc)
  252.     {
  253.         var this_ = this;
  254.         this.getLoginText(
  255.             request,
  256.             // probe whether server is accessible and responds login text:
  257.             function getLoginText_resp(err, loginText) {
  258.                 if (err) {
  259.                     respFunc(err);
  260.                     return;
  261.                 }
  262.                 // lookup password manager, then try login or prompt/login:
  263.                 log("attempting to get a session id for " + this_.sessionUri.spec, this_);
  264.                 
  265.                 if (!this_.sessionUri.schemeIs("https") &&
  266.                     !confirmInsecureLogin(this_.sessionUri)) {
  267.                     log("user rejected insecure login on " + this_.sessionUri.spec, this_);
  268.                     respFunc(new Components.Exception(
  269.                                  "Login failed. Invalid session ID.",
  270.                                  calIWcapErrors.WCAP_LOGIN_FAILED));
  271.                     return;
  272.                 }
  273.                 
  274.                 var outUser = { value: this_.credentials.userId };
  275.                 var outPW = { value: this_.credentials.pw };
  276.                 var outSavePW = { value: false };
  277.                 
  278.                 // pw mgr host names must not have a trailing slash
  279.                 var passwordManager =
  280.                     Components.classes["@mozilla.org/passwordmanager;1"]
  281.                               .getService(Components.interfaces.nsIPasswordManager);
  282.                 var pwHost = this_.uri.spec;
  283.                 if (pwHost[pwHost.length - 1] == '/')
  284.                     pwHost = pwHost.substr(0, pwHost.length - 1);
  285.                 
  286.                 if (!outPW.value) { // lookup pw manager
  287.                     log("looking in pw db for: " + pwHost, this_);
  288.                     try {
  289.                         var enumerator = passwordManager.enumerator;
  290.                         while (enumerator.hasMoreElements()) {
  291.                             var pwEntry = enumerator.getNext().QueryInterface(
  292.                                 Components.interfaces.nsIPassword);
  293.                             if (LOG_LEVEL > 1) {
  294.                                 log("pw entry:\n\thost=" + pwEntry.host +
  295.                                     "\n\tuser=" + pwEntry.user, this_);
  296.                             }
  297.                             if (pwEntry.host == pwHost) {
  298.                                 // found an entry matching URI:
  299.                                 outUser.value = pwEntry.user;
  300.                                 outPW.value = pwEntry.password;
  301.                                 log("password entry found for host " + pwHost +
  302.                                     "\nuser is " + outUser.value, this_);
  303.                                 break;
  304.                             }
  305.                         }
  306.                     }
  307.                     catch (exc) { // just log error
  308.                         logError("[password manager lookup] " + errorToString(exc), this_);
  309.                     }
  310.                 }
  311.                 
  312.                 function promptAndLoginLoop_resp(err, sessionId) {
  313.                     if (getResultCode(err) == calIWcapErrors.WCAP_LOGIN_FAILED) {
  314.                         log("prompting for user/pw...", this_);
  315.                         var prompt = getWindowWatcher().getNewPrompter(null);
  316.                         if (prompt.promptUsernameAndPassword(
  317.                                 calGetString("wcap", "loginDialog.label"),
  318.                                 loginText, outUser, outPW,
  319.                                 getPref("signon.rememberSignons", true)
  320.                                 ? calGetString("wcap", "loginDialog.check.text")
  321.                                 : null, outSavePW)) {
  322.                             this_.login(request, promptAndLoginLoop_resp,
  323.                                         outUser.value, outPW.value);
  324.                         }
  325.                         else {
  326.                             log("login prompt cancelled.", this_);
  327.                             respFunc(new Components.Exception(
  328.                                          "Login failed. Invalid session ID.",
  329.                                          calIWcapErrors.WCAP_LOGIN_FAILED));
  330.                         }
  331.                     }
  332.                     else if (err)
  333.                         respFunc(err);
  334.                     else {
  335.                         if (outSavePW.value) {
  336.                             // so try to remove old pw from db first:
  337.                             try {
  338.                                 passwordManager.removeUser(pwHost, outUser.value);
  339.                                 log("removed from pw db: " + pwHost, this_);
  340.                             }
  341.                             catch (exc) {
  342.                             }
  343.                             try { // to save pw under session uri:
  344.                                 passwordManager.addUser(pwHost, outUser.value, outPW.value);
  345.                                 log("added to pw db: " + pwHost, this_);
  346.                             }
  347.                             catch (exc) {
  348.                                 logError("[adding pw to db] " + errorToString(exc), this_);
  349.                             }
  350.                         }
  351.                         this_.credentials.userId = outUser.value;
  352.                         this_.credentials.pw = outPW.value;
  353.                         this_.setupSession(sessionId,
  354.                                            request,
  355.                                            function setupSession_resp(err) {
  356.                                                respFunc(err, sessionId);
  357.                                            });
  358.                     }
  359.                 }
  360.                     
  361.                 if (outPW.value) {
  362.                     this_.login(request, promptAndLoginLoop_resp,
  363.                                 outUser.value, outPW.value);
  364.                 }
  365.                 else {
  366.                     promptAndLoginLoop_resp(calIWcapErrors.WCAP_LOGIN_FAILED);
  367.                 }
  368.             });
  369.     },
  370.     
  371.     login: function calWcapSession_login(request, respFunc, user, pw)
  372.     {
  373.         var this_ = this;
  374.         issueNetworkRequest(
  375.             request,
  376.             function netResp(err, str) {
  377.                 var sessionId;
  378.                 try {
  379.                     if (err)
  380.                         throw err;
  381.                     // currently, xml parsing at an early stage during
  382.                     // process startup does not work reliably, so use
  383.                     // libical parsing for now:
  384.                     var icalRootComp = stringToIcal(str);
  385.                     var prop = icalRootComp.getFirstProperty("X-NSCP-WCAP-SESSION-ID");
  386.                     if (!prop) {
  387.                         throw new Components.Exception(
  388.                             "missing X-NSCP-WCAP-SESSION-ID in\n" + str);
  389.                     }
  390.                     sessionId = prop.value;
  391.                     log("login succeeded: " + sessionId, this_);
  392.                 }
  393.                 catch (exc) {
  394.                     err = exc;
  395.                     var rc = getResultCode(exc);
  396.                     if (rc == calIWcapErrors.WCAP_LOGIN_FAILED) {
  397.                         logError(exc, this_); // log login failure
  398.                     }
  399.                     else if (getErrorModule(rc) == NS_ERROR_MODULE_NETWORK) {
  400.                         // server seems unavailable:
  401.                         err = new Components.Exception(
  402.                             calGetString( "wcap", "accessingServerFailedError.text",
  403.                                           [this_.sessionUri.hostPort]),
  404.                             exc);
  405.                     }
  406.                 }
  407.                 respFunc(err, sessionId);
  408.             },
  409.             this_.sessionUri.spec + "login.wcap?fmt-out=text%2Fcalendar&user=" +
  410.             encodeURIComponent(user) + "&password=" + encodeURIComponent(pw),
  411.             false /* no logging */);
  412.     },
  413.     
  414.     logout: function calWcapSession_logout(listener)
  415.     {
  416.         var this_ = this;
  417.         var request = new calWcapRequest(
  418.             function logout_resp(request, err) {
  419.                 if (err)
  420.                     logError(err, this_);
  421.                 else
  422.                     log("logout succeeded.", this_);
  423.                 if (listener)
  424.                     listener.onResult(request, err);
  425.             },
  426.             log("logout", this));
  427.         
  428.         var url = null;
  429.         if (this.m_sessionId) {
  430.             log("attempting to log out...", this);
  431.             // although io service's offline flag is already
  432.             // set BEFORE notification
  433.             // (about to go offline, nsIOService.cpp).
  434.             // WTF.
  435.             url = (this.sessionUri.spec + "logout.wcap?fmt-out=text%2Fxml&id=" + this.m_sessionId);
  436.             this.m_sessionId = null;
  437.             getFreeBusyService().removeProvider(this);
  438.         }
  439.         this.m_credentials = null;
  440.         
  441.         if (url) {
  442.             issueNetworkRequest(
  443.                 request,
  444.                 function netResp(err, str) {
  445.                     if (err)
  446.                         throw err;
  447.                     stringToXml(str, -1 /* logout successfull */);
  448.                 }, url);
  449.         }
  450.         else {
  451.             request.execRespFunc();
  452.         }
  453.         return request;
  454.     },
  455.     
  456.     getLoginText: function calWcapSession_getLoginText(request, respFunc)
  457.     {
  458.         // currently, xml parsing at an early stage during process startup
  459.         // does not work reliably, so use libical:
  460.         var this_ = this;
  461.         issueNetworkRequest(
  462.             request,
  463.             function netResp(err, str) {
  464.                 var loginText;
  465.                 try {
  466.                     var icalRootComp;
  467.                     if (!err) {
  468.                         try {
  469.                             icalRootComp = stringToIcal(str);
  470.                         }
  471.                         catch (exc) {
  472.                             err = exc;
  473.                         }
  474.                     }
  475.                     if (err) { // soft error; request denied etc.
  476.                                // map into localized message:
  477.                         throw new Components.Exception(
  478.                             calGetString("wcap", "accessingServerFailedError.text",
  479.                                          [this_.sessionUri.hostPort]),
  480.                             calIWcapErrors.WCAP_LOGIN_FAILED);
  481.                     }
  482.                     var prop = icalRootComp.getFirstProperty("X-NSCP-WCAPVERSION");
  483.                     if (!prop)
  484.                         throw new Components.Exception("missing X-NSCP-WCAPVERSION!");
  485.                     var wcapVersion = parseInt(prop.value);
  486.                     if (wcapVersion < 3) {
  487.                         var strVers = prop.value;
  488.                         var vars = [this_.sessionUri.hostPort];
  489.                         prop = icalRootComp.getFirstProperty("PRODID");
  490.                         vars.push(prop ? prop.value : "<unknown>");
  491.                         prop = icalRootComp.getFirstProperty("X-NSCP-SERVERVERSION");
  492.                         vars.push(prop ? prop.value : "<unknown>");
  493.                         vars.push(strVers);
  494.                         
  495.                         var prompt = getWindowWatcher().getNewPrompter(null);
  496.                         var labelText = calGetString(
  497.                             "wcap", "insufficientWcapVersionConfirmation.label");
  498.                         if (!prompt.confirm(
  499.                                 labelText,
  500.                                 calGetString("wcap", "insufficientWcapVersionConfirmation.text", vars))) {
  501.                             throw new Components.Exception(
  502.                                 labelText, calIWcapErrors.WCAP_LOGIN_FAILED);
  503.                         }
  504.                     }
  505.                     loginText = calGetString("wcap", "loginDialog.text", [this_.sessionUri.hostPort]);
  506.                 }
  507.                 catch (exc) {
  508.                     err = exc;
  509.                 }
  510.                 respFunc(err, loginText);
  511.             },
  512.             this_.sessionUri.spec + "version.wcap?fmt-out=text%2Fcalendar");
  513.     },
  514.  
  515.     setupSession:
  516.     function calWcapSession_setupSession(sessionId, request_, respFunc)
  517.     {
  518.         var this_ = this;
  519.         var request = new calWcapRequest(
  520.             function setupSession_resp(request_, err) {
  521.                 log("setupSession_resp finished: " + errorToString(err), this_);
  522.                 respFunc(err);
  523.             },
  524.             log("setupSession", this));
  525.         request_.attachSubRequest(request);
  526.         
  527.         request.lockPending();
  528.         try {
  529.             var this_ = this;
  530.             this.issueNetworkRequest_(
  531.                 request,
  532.                 function userprefs_resp(err, data) {
  533.                     if (err)
  534.                         throw err;
  535.                     this_.credentials.userPrefs = data;
  536.                     log("installed user prefs.", this_);
  537.                     
  538.                     // get calprops for all registered calendars:                        
  539.                     var cals = this_.getRegisteredCalendars();
  540.  
  541.                     var calManager = getCalendarManager();
  542.                     var calprops_resp = null;
  543.                     var defaultCal = this_.defaultCalendar;
  544.                     if (defaultCal && cals[defaultCal.calId] && // default calendar is registered
  545.                         getPref("calendar.wcap.subscriptions", false) &&
  546.                         !calManager.getCalendarPref(defaultCal, "subscriptions_registered")) {
  547.                         
  548.                         var hasSubscriptions = false;
  549.                         // post register subscribed calendars:
  550.                         var list = this_.getUserPreferences("X-NSCP-WCAP-PREF-icsSubscribed");
  551.                         for each (var item in list) {
  552.                             var ar = item.split(',');
  553.                             // ',', '$' are not encoded. ',' can be handled here. WTF.
  554.                             for each (var a in ar) {
  555.                                 var dollar = a.indexOf('$');
  556.                                 if (dollar >= 0) {
  557.                                     var calId = a.substring(0, dollar);
  558.                                     if (calId != this_.defaultCalId) {
  559.                                         cals[calId] = null;
  560.                                         hasSubscriptions = true;
  561.                                     }
  562.                                 }
  563.                             }
  564.                         }
  565.                         
  566.                         if (hasSubscriptions) {
  567.                             calprops_resp = function(cal) {
  568.                                 if (cal.isDefaultCalendar) {
  569.                                     // tweak name:
  570.                                     calManager.setCalendarPref(cal, "name", cal.displayName);
  571.                                 }
  572.                                 else {
  573.                                     log("registering subscribed calendar: " + cal.calId, this_);
  574.                                     calManager.registerCalendar(cal);
  575.                                 }
  576.                             }
  577.                             // do only once:
  578.                             calManager.setCalendarPref(defaultCal,
  579.                                                        "shared_context", this_.m_contextId);
  580.                             calManager.setCalendarPref(defaultCal,
  581.                                                        "account_name", defaultCal.name);
  582.                             calManager.setCalendarPref(defaultCal,
  583.                                                        "subscriptions_registered", "1");
  584.                         }
  585.                     }
  586.                     
  587.                     if (getPref("calendar.wcap.no_get_calprops", false)) {
  588.                         // hack around the get/search calprops mess:
  589.                         this_.installCalProps_search_calprops(calprops_resp, sessionId, cals, request);
  590.                     }
  591.                     else {
  592.                         this_.installCalProps_get_calprops(calprops_resp, sessionId, cals, request);
  593.                     }
  594.                 },
  595.                 stringToXml, "get_userprefs",
  596.                 "&fmt-out=text%2Fxml&userid=" + encodeURIComponent(this.credentials.userId),
  597.                 sessionId);
  598.             this.installServerTimeDiff(sessionId, request);
  599.             this.installServerTimezones(sessionId, request);
  600.         }
  601.         finally {
  602.             request.unlockPending();
  603.         }
  604.     },
  605.     
  606.     installCalProps_get_calprops:
  607.     function calWcapSession_installCalProps_get_calprops(respFunc, sessionId, cals, request)
  608.     {
  609.         var this_ = this;
  610.         function calprops_resp(err, data) {
  611.             if (err)
  612.                 throw err;
  613.             // string to xml converter func without WCAP errno check:
  614.             if (!data || data.length == 0) { // assuming time-out
  615.                 throw new Components.Exception("Login failed. Invalid session ID.",
  616.                                                calIWcapErrors.WCAP_LOGIN_FAILED);
  617.             }
  618.             var xml = getDomParser().parseFromString(data, "text/xml");
  619.             var nodeList = xml.getElementsByTagName("iCal");
  620.             for (var i = 0; i < nodeList.length; ++i) {
  621.                 try {
  622.                     var node = nodeList.item(i);
  623.                     checkWcapXmlErrno(node);
  624.                     var ar = filterXmlNodes("X-NSCP-CALPROPS-RELATIVE-CALID", node);
  625.                     if (ar.length > 0) {
  626.                         var calId = ar[0];
  627.                         var cal = cals[calId];
  628.                         if (cal === null) {
  629.                             cal = new calWcapCalendar(this_);
  630.                             var uri = this_.uri.clone();
  631.                             uri.path += ("?calid=" + encodeURIComponent(calId));
  632.                             cal.uri = uri;
  633.                         }
  634.                         if (cal) {
  635.                             cal.m_calProps = node;
  636.                             if (respFunc) {
  637.                                 respFunc(cal);
  638.                             }
  639.                         }
  640.                     }
  641.                 }
  642.                 catch (exc) { // ignore but log any errors on subscribed calendars:
  643.                     logError(exc, this_);
  644.                 }
  645.             }
  646.         }
  647.  
  648.         var calidParam = "";
  649.         for (var calId in cals) {
  650.             if (calidParam.length > 0)
  651.                 calidParam += ";";
  652.             calidParam += encodeURIComponent(calId);
  653.         }
  654.         this_.issueNetworkRequest_(request, calprops_resp,
  655.                                    null, "get_calprops",
  656.                                    "&fmt-out=text%2Fxml&calid=" + calidParam,
  657.                                    sessionId);
  658.     },
  659.  
  660.     installCalProps_search_calprops:
  661.     function calWcapSession_installCalProps_search_calprops(respFunc, sessionId, cals, request)
  662.     {
  663.         var this_ = this;
  664.         var retrievedCals = {};
  665.         var issuedSearchRequests = {};
  666.         for (var calId in cals) {
  667.             if (!retrievedCals[calId]) {
  668.                 var listener = {
  669.                     onResult: function search_onResult(request, result) {
  670.                         try {
  671.                             if (!request.success)
  672.                                 throw request.status;
  673.                             if (result.length < 1)
  674.                                 throw Components.results.NS_ERROR_UNEXPECTED;
  675.                             for each (var cal in result) {
  676.                                 // user may have dangling users referred in his subscription list, so
  677.                                 // retrieve each by each, don't break:
  678.                                 try {
  679.                                     var calId = cal.calId;
  680.                                     if ((cals[calId] !== undefined) && !retrievedCals[calId]) {
  681.                                         retrievedCals[calId] = cal;
  682.                                         if (respFunc) {
  683.                                             respFunc(cal);
  684.                                         }
  685.                                     }
  686.                                 }
  687.                                 catch (exc) { // ignore but log any errors on subscribed calendars:
  688.                                     logError(exc, this_);
  689.                                 }
  690.                             }
  691.                         }
  692.                         catch (exc) { // ignore but log any errors on subscribed calendars:
  693.                             logError(exc, this_);
  694.                         }
  695.                     }
  696.                 };
  697.                 
  698.                 var colon = calId.indexOf(':');
  699.                 if (colon >= 0) // searching for secondary calendars doesn't work. WTF.
  700.                     calId = calId.substring(0, colon);
  701.                 if (!issuedSearchRequests[calId]) {
  702.                     issuedSearchRequests[calId] = true;
  703.                     this.searchForCalendars(
  704.                         calId,
  705.                         calIWcapSession.SEARCH_STRING_EXACT |
  706.                         calIWcapSession.SEARCH_INCLUDE_CALID |
  707.                         // else searching for secondary calendars doesn't work:
  708.                         calIWcapSession.SEARCH_INCLUDE_OWNER,
  709.                         20, listener);
  710.                 }
  711.             }
  712.         }
  713.     },
  714.  
  715.     installServerTimeDiff:
  716.     function calWcapSession_installServerTimeDiff(sessionId, request)
  717.     {
  718.         var this_ = this;
  719.         this.issueNetworkRequest_(
  720.             request,
  721.             function netResp(err, data) {
  722.                 if (err)
  723.                     throw err;
  724.                 // xxx todo: think about
  725.                 // assure that locally calculated server time is smaller
  726.                 // than the current (real) server time:
  727.                 var localTime = getTime();
  728.                 var serverTime = getDatetimeFromIcalProp(
  729.                     data.getFirstProperty("X-NSCP-WCAPTIME"));
  730.                 this_.m_serverTimeDiff = serverTime.subtractDate(localTime);
  731.                 log("server time diff is: " + this_.m_serverTimeDiff, this_);
  732.             },
  733.             stringToIcal, "gettime", "&fmt-out=text%2Fcalendar",
  734.             sessionId);
  735.     },
  736.     
  737.     installServerTimezones:
  738.     function calWcapSession_installServerTimezones(sessionId, request)
  739.     {
  740.         this.m_serverTimezones = [];
  741.         var this_ = this;
  742.         this_.issueNetworkRequest_(
  743.             request,
  744.             function netResp(err, data) {
  745.                 if (err)
  746.                     throw err;
  747.                 var tzids = [];
  748.                 var icsService = getIcsService();
  749.                 forEachIcalComponent(
  750.                     data, "VTIMEZONE",
  751.                     function eachComp(subComp) {
  752.                         try {
  753.                             var tzCal = icsService.createIcalComponent("VCALENDAR");
  754.                             subComp = subComp.clone();
  755.                             tzCal.addSubcomponent(subComp);
  756.                             icsService.addTimezone(tzCal, "", "");
  757.                             this_.m_serverTimezones.push(
  758.                                 subComp.getFirstProperty("TZID").value);
  759.                         }
  760.                         catch (exc) { // ignore but errors:
  761.                             logError(exc, this_);
  762.                         }
  763.                     });
  764.                 log("installed timezones.", this_);
  765.             },
  766.             stringToIcal, "get_all_timezones", "&fmt-out=text%2Fcalendar",
  767.             sessionId);
  768.     },
  769.     
  770.     getCommandUrl: function calWcapSession_getCommandUrl(wcapCommand, params, sessionId)
  771.     {
  772.         var url = this.sessionUri.spec;
  773.         url += (wcapCommand + ".wcap?appid=mozilla-calendar&id=");
  774.         url += sessionId;
  775.         url += params;
  776.         return url;
  777.     },
  778.  
  779.     issueNetworkRequest: function calWcapSession_issueNetworkRequest(
  780.         request, respFunc, dataConvFunc, wcapCommand, params)
  781.     {
  782.         var this_ = this;
  783.         function getSessionId_resp(err, sessionId) {
  784.             if (err)
  785.                 respFunc(err);
  786.             else {
  787.                 // else have session uri and id:
  788.                 this_.issueNetworkRequest_(
  789.                     request,
  790.                     function issueNetworkRequest_resp(err, data) {
  791.                         // timeout?
  792.                         if (getResultCode(err) == calIWcapErrors.WCAP_LOGIN_FAILED) {
  793.                             // try again:
  794.                             this_.getSessionId(
  795.                                 request,
  796.                                 getSessionId_resp,
  797.                                 sessionId/* (old) timed-out session */);
  798.                             return;
  799.                         }
  800.                         respFunc(err, data);
  801.                     },
  802.                     dataConvFunc, wcapCommand, params, sessionId);
  803.             }
  804.         }
  805.         this.getSessionId(request, getSessionId_resp);
  806.     },
  807.     
  808.     issueNetworkRequest_: function calWcapSession_issueNetworkRequest_(
  809.         request, respFunc, dataConvFunc, wcapCommand, params, sessionId)
  810.     {
  811.         var url = this.getCommandUrl(wcapCommand, params, sessionId);
  812.         issueNetworkRequest(
  813.             request,
  814.             function netResp(err, str) {
  815.                 var data;
  816.                 if (!err) {
  817.                     try {
  818.                         if (dataConvFunc)
  819.                             data = dataConvFunc(str);
  820.                         else
  821.                             data = str;
  822.                     }
  823.                     catch (exc) {
  824.                         err = exc;
  825.                     }
  826.                 }
  827.                 respFunc(err, data);
  828.             }, url);
  829.     },
  830.     
  831.     m_credentials: null,
  832.     get credentials() {
  833.         if (!this.m_credentials) {
  834.             this.m_credentials = {
  835.                 userId: "",
  836.                 pw: "",
  837.                 userPrefs: null
  838.             };
  839.         }
  840.         return this.m_credentials;
  841.     },
  842.     
  843.     // calIWcapSession:
  844.  
  845.     m_contextId: null,
  846.     m_uri: null,
  847.     m_sessionUri: null,
  848.     get uri() { return this.m_uri; },
  849.     get sessionUri() { return this.m_sessionUri; },
  850.     
  851.     get userId() { return this.credentials.userId; },
  852.     
  853.     get defaultCalId() {
  854.         var list = this.getUserPreferences("X-NSCP-WCAP-PREF-icsCalendar");
  855.         var id = null;
  856.         for each (var item in list) {
  857.             if (item.length > 0) {
  858.                 id = item;
  859.                 break;
  860.             }
  861.         }
  862.         return (id ? id : this.credentials.userId);
  863.     },
  864.     
  865.     get isLoggedIn() {
  866.         return (this.m_sessionId != null);
  867.     },
  868.     
  869.     defaultCalendar: null,
  870.     
  871.     belongsTo: function calWcapSession_belongsTo(cal) {
  872.         try {
  873.             cal = cal.QueryInterface(calIWcapCalendar).wrappedJSObject;
  874.             if (cal && (cal.session.m_contextId == this.m_contextId)) {
  875.                 return cal;
  876.             }
  877.         }
  878.         catch (exc) {
  879.         }
  880.         return null;
  881.     },
  882.  
  883.     getRegisteredCalendars: function calWcapSession_getRegisteredCalendars() {
  884.         var registeredCalendars = {};
  885.         var cals = getCalendarManager().getCalendars({});
  886.         for each (var cal in cals) {
  887.             cal = this.belongsTo(cal);
  888.             if (cal) {
  889.                 registeredCalendars[cal.calId] = cal;
  890.             }
  891.         }
  892.         return registeredCalendars;
  893.     },
  894.  
  895.     getUserPreferences: function calWcapSession_getUserPreferences(prefName) {
  896.         var prefs = filterXmlNodes(prefName, this.credentials.userPrefs);
  897.         return prefs;
  898.     },
  899.     
  900.     get defaultAlarmStart() {
  901.         var alarmStart = null;
  902.         var ar = this.getUserPreferences("X-NSCP-WCAP-PREF-ceDefaultAlarmStart");
  903.         if (ar.length > 0 && ar[0].length > 0) {
  904.             // workarounding cs duration bug, missing "T":
  905.             var dur = ar[0].replace(/(^P)(\d+[HMS]$)/, "$1T$2");
  906.             alarmStart = new CalDuration();
  907.             alarmStart.icalString = dur;
  908.             alarmStart.isNegative = !alarmStart.isNegative;
  909.         }
  910.         return alarmStart;
  911.     },
  912.     
  913.     getDefaultAlarmEmails: function calWcapSession_getDefaultAlarmEmails(out_count)
  914.     {
  915.         var ret = [];
  916.         var ar = this.getUserPreferences("X-NSCP-WCAP-PREF-ceDefaultAlarmEmail");
  917.         if (ar.length > 0 && ar[0].length > 0) {
  918.             for each (var i in ar) {
  919.                 ret = ret.concat( i.split(/[;,]/).map(trimString) );
  920.             }
  921.         }
  922.         out_count.value = ret.length;
  923.         return ret;
  924.     },
  925.     
  926.     searchForCalendars:
  927.     function calWcapSession_searchForCalendars(searchString, searchOptions, maxResults, listener)
  928.     {
  929.         var this_ = this;
  930.         var request = new calWcapRequest(
  931.             function searchForCalendars_resp(request, err, data) {
  932.                 if (err && getResultCode(err) != calIErrors.OPERATION_CANCELLED)
  933.                     this_.notifyError(err);
  934.                 if (listener)
  935.                     listener.onResult(request, data);
  936.             },
  937.             log("searchForCalendars, searchString=" + searchString, this));
  938.         
  939.         try {
  940.             var registeredCalendars = this.getRegisteredCalendars();
  941.             
  942.             var params = ("&fmt-out=text%2Fxml&search-string=" +
  943.                           encodeURIComponent(searchString));
  944.             params += ("&searchOpts=" + (searchOptions & 3).toString(10));
  945.             if (maxResults > 0)
  946.                 params += ("&maxResults=" + maxResults);
  947.             if (searchOptions & calIWcapSession.SEARCH_INCLUDE_CALID)
  948.                 params += "&calid=1";
  949.             if (searchOptions & calIWcapSession.SEARCH_INCLUDE_NAME)
  950.                 params += "&name=1";
  951.             if (searchOptions & calIWcapSession.SEARCH_INCLUDE_OWNER)
  952.                 params += "&primaryOwner=1";
  953.             
  954.             this.issueNetworkRequest(
  955.                 request,
  956.                 function searchForCalendars_netResp(err, data) {
  957.                     if (err)
  958.                         throw err;
  959.                     // string to xml converter func without WCAP errno check:
  960.                     if (!data || data.length == 0) { // assuming time-out
  961.                         throw new Components.Exception("Login failed. Invalid session ID.",
  962.                                                        calIWcapErrors.WCAP_LOGIN_FAILED);
  963.                     }
  964.                     var xml = getDomParser().parseFromString(data, "text/xml");
  965.                     var ret = [];
  966.                     var nodeList = xml.getElementsByTagName("iCal");
  967.                     for ( var i = 0; i < nodeList.length; ++i ) {
  968.                         var node = nodeList.item(i);
  969.                         try {
  970.                             checkWcapXmlErrno(node);
  971.                             var ar = filterXmlNodes("X-NSCP-CALPROPS-RELATIVE-CALID", node);
  972.                             if (ar.length > 0) {
  973.                                 var calId = ar[0];
  974.                                 var cal = registeredCalendars[calId];
  975.                                 if (cal) {
  976.                                     cal.m_calProps = node; // update calprops
  977.                                 }
  978.                                 else {
  979.                                     cal = new calWcapCalendar(this_, node);
  980.                                     var uri = this_.uri.clone();
  981.                                     uri.path += ("?calid=" + encodeURIComponent(calId));
  982.                                     cal.uri = uri;
  983.                                 }
  984.                                 ret.push(cal);
  985.                             }
  986.                         }
  987.                         catch (exc) {
  988.                             switch (getResultCode(exc)) {
  989.                             case calIWcapErrors.WCAP_NO_ERRNO: // workaround
  990.                             case calIWcapErrors.WCAP_ACCESS_DENIED_TO_CALENDAR:
  991.                                 log("searchForCalendars_netResp() ignored error: " +
  992.                                     errorToString(exc), this_);
  993.                                 break;
  994.                             default:
  995.                                 this_.notifyError(exc);
  996.                                 break;
  997.                             }
  998.                         }
  999.                     }
  1000.                     log("search done. number of found calendars: " + ret.length, this_);
  1001.                     request.execRespFunc(null, ret);
  1002.                 },
  1003.                 null, "search_calprops", params);
  1004.         }
  1005.         catch (exc) {
  1006.             request.execRespFunc(exc);
  1007.         }
  1008.         return request;
  1009.     },
  1010.  
  1011.     // calIFreeBusyProvider:
  1012.     getFreeBusyIntervals: function calWcapCalendar_getFreeBusyIntervals(
  1013.         calId, rangeStart, rangeEnd, busyTypes, listener)
  1014.     {
  1015.         // assure DATETIMEs:
  1016.         if (rangeStart && rangeStart.isDate) {
  1017.             rangeStart = rangeStart.clone();
  1018.             rangeStart.isDate = false;
  1019.         }
  1020.         if (rangeEnd && rangeEnd.isDate) {
  1021.             rangeEnd = rangeEnd.clone();
  1022.             rangeEnd.isDate = false;
  1023.         }
  1024.         var zRangeStart = getIcalUTC(rangeStart);
  1025.         var zRangeEnd = getIcalUTC(rangeEnd);
  1026.         
  1027.         var this_ = this;
  1028.         var request = new calWcapRequest(
  1029.             function _resp(request, err, data) {
  1030.                 var rc = getResultCode(err);
  1031.                 switch (rc) {
  1032.                 case calIErrors.OPERATION_CANCELLED:
  1033.                 case calIWcapErrors.WCAP_NO_ERRNO: // workaround
  1034.                 case calIWcapErrors.WCAP_ACCESS_DENIED_TO_CALENDAR:
  1035.                 case calIWcapErrors.WCAP_CALENDAR_DOES_NOT_EXIST:
  1036.                     log("getFreeBusyIntervals_resp() error: " + errorToString(err), this_);
  1037.                     break;
  1038.                 default:
  1039.                     if (!Components.isSuccessCode(rc))
  1040.                         this_.notifyError(err);
  1041.                     break;
  1042.                 }
  1043.                 if (listener)
  1044.                     listener.onResult(request, data);
  1045.             },
  1046.             log("getFreeBusyIntervals():\n\tcalId=" + calId +
  1047.                 "\n\trangeStart=" + zRangeStart + ",\n\trangeEnd=" + zRangeEnd, this));
  1048.         
  1049.         try {
  1050.             var params = ("&calid=" + encodeURIComponent(calId));
  1051.             params += ("&busyonly=" + ((busyTypes & calIFreeBusyInterval.FREE) ? "0" : "1"));
  1052.             params += ("&dtstart=" + zRangeStart);
  1053.             params += ("&dtend=" + zRangeEnd);
  1054.             params += "&fmt-out=text%2Fxml";
  1055.  
  1056.             // cannot use stringToXml here, because cs 6.3 returns plain nothing
  1057.             // on invalid user freebusy requests. WTF.
  1058.             function stringToXml_(data) {
  1059.                 if (!data || data.length == 0) { // assuming invalid user
  1060.                     throw new Components.Exception(
  1061.                         wcapErrorToString(calIWcapErrors.WCAP_CALENDAR_DOES_NOT_EXIST),
  1062.                         calIWcapErrors.WCAP_CALENDAR_DOES_NOT_EXIST);
  1063.                 }
  1064.                 return stringToXml(data);
  1065.             }
  1066.             this.issueNetworkRequest(
  1067.                 request,
  1068.                 function net_resp(err, xml) {
  1069.                     if (err)
  1070.                         throw err;
  1071.                     if (LOG_LEVEL > 0) {
  1072.                         log("getFreeBusyIntervals net_resp(): " +
  1073.                             getWcapRequestStatusString(xml), this_);
  1074.                     }
  1075.                     if (listener) {
  1076.                         var ret = [];
  1077.                         var nodeList = xml.getElementsByTagName("FB");
  1078.                         
  1079.                         var fbTypeMap = {};
  1080.                         fbTypeMap["FREE"] = calIFreeBusyInterval.FREE;
  1081.                         fbTypeMap["BUSY"] = calIFreeBusyInterval.BUSY;
  1082.                         fbTypeMap["BUSY-UNAVAILABLE"] = calIFreeBusyInterval.BUSY_UNAVAILABLE;
  1083.                         fbTypeMap["BUSY-TENTATIVE"] = calIFreeBusyInterval.BUSY_TENTATIVE;
  1084.                         
  1085.                         for (var i = 0; i < nodeList.length; ++i) {
  1086.                             var node = nodeList.item(i);
  1087.                             var fbType = fbTypeMap[node.attributes.getNamedItem("FBTYPE").nodeValue];
  1088.                             if (fbType & busyTypes) {
  1089.                                 var str = node.textContent;
  1090.                                 var slash = str.indexOf('/');
  1091.                                 var period = new CalPeriod();
  1092.                                 period.start = getDatetimeFromIcalString(str.substr(0, slash));
  1093.                                 period.end = getDatetimeFromIcalString(str.substr(slash + 1));
  1094.                                 period.makeImmutable();
  1095.                                 var fbInterval = {
  1096.                                     QueryInterface: function fbInterval_QueryInterface(iid) {
  1097.                                         ensureIID([calIFreeBusyInterval, nsISupports], iid);
  1098.                                         return this;
  1099.                                     },
  1100.                                     calId: calId,
  1101.                                     interval: period,
  1102.                                     freeBusyType: fbType
  1103.                                 };
  1104.                                 ret.push(fbInterval);
  1105.                             }
  1106.                         }
  1107.                         request.execRespFunc(null, ret);
  1108.                     }
  1109.                 },
  1110.                 stringToXml_, "get_freebusy", params);
  1111.         }
  1112.         catch (exc) {
  1113.             request.execRespFunc(exc);
  1114.         }
  1115.         return request;
  1116.     },
  1117.     
  1118.     // nsIObserver:
  1119.     observe: function calWcapSession_observer(subject, topic, data)
  1120.     {
  1121.         log("observing: " + topic + ", data: " + data, this);
  1122.         if (topic == "quit-application") {
  1123.             g_bShutdown = true;
  1124.             this.logout(null);
  1125.             // xxx todo: valid upon notification?
  1126.             getCalendarManager().removeObserver(this);
  1127.             var observerService = Components.classes["@mozilla.org/observer-service;1"]
  1128.                                             .getService(Components.interfaces.nsIObserverService);
  1129.             observerService.removeObserver(this, "quit-application");
  1130.         }
  1131.     },
  1132.     
  1133.     // calICalendarManagerObserver:
  1134.     
  1135.     // called after the calendar is registered
  1136.     onCalendarRegistered: function calWcapSession_onCalendarRegistered(cal)
  1137.     {
  1138.         try {
  1139.             // make sure the calendar belongs to this session:
  1140.             if (this.belongsTo(cal)) {
  1141.  
  1142.                 var calManager = getCalendarManager();
  1143.                 function assureDefault(pref, val) {
  1144.                     if (!calManager.getCalendarPref(cal, pref)) {
  1145.                         calManager.setCalendarPref(cal, pref, val);
  1146.                     }
  1147.                 }
  1148.                 
  1149.                 assureDefault("shared_context", this.m_contextId);
  1150.                 assureDefault("name", cal.name);
  1151.                 
  1152.                 const s_colors = ["#FFCCCC", "#FFCC99", "#FFFF99", "#FFFFCC", "#99FF99",
  1153.                                   "#99FFFF", "#CCFFFF", "#CCCCFF", "#FFCCFF", "#FF6666",
  1154.                                   "#FF9966", "#FFFF66", "#FFFF33", "#66FF99", "#33FFFF",
  1155.                                   "#66FFFF", "#9999FF", "#FF99FF", "#FF0000", "#FF9900",
  1156.                                   "#FFCC66", "#FFFF00", "#33FF33", "#66CCCC", "#33CCFF",
  1157.                                   "#6666CC", "#CC66CC", "#CC0000", "#FF6600", "#FFCC33",
  1158.                                   "#FFCC00", "#33CC00", "#00CCCC", "#3366FF", "#6633FF",
  1159.                                   "#CC33CC", "#990000", "#CC6600", "#CC9933", "#999900",
  1160.                                   "#009900", "#339999", "#3333FF", "#6600CC", "#993399",
  1161.                                   "#660000", "#993300", "#996633", "#666600", "#006600",
  1162.                                   "#336666", "#000099", "#333399", "#663366", "#330000",
  1163.                                   "#663300", "#663333", "#333300", "#003300", "#003333",
  1164.                                   "#000066", "#330099", "#330033"];
  1165.                 assureDefault("color", s_colors[(new Date()).getUTCMilliseconds() % s_colors.length]);
  1166.             }
  1167.         }
  1168.         catch (exc) { // never break the listener chain
  1169.             this.notifyError(exc);
  1170.         }
  1171.     },
  1172.     
  1173.     // called before the unregister actually takes place
  1174.     onCalendarUnregistering: function calWcapSession_onCalendarUnregistering(cal)
  1175.     {
  1176.         try {
  1177.             // make sure the calendar belongs to this session and is the default calendar,
  1178.             // then remove all subscribed calendars:
  1179.             cal = this.belongsTo(cal);
  1180.             if (cal && cal.isDefaultCalendar) {
  1181.                 getFreeBusyService().removeProvider(this);
  1182.                 var registeredCalendars = this.getRegisteredCalendars();
  1183.                 for each (var regCal in registeredCalendars) {
  1184.                     try {
  1185.                         if (!regCal.isDefaultCalendar) {
  1186.                             getCalendarManager().unregisterCalendar(regCal);
  1187.                         }
  1188.                     }
  1189.                     catch (exc) {
  1190.                         this.notifyError(exc);
  1191.                     }
  1192.                 }
  1193.             }
  1194.         }
  1195.         catch (exc) { // never break the listener chain
  1196.             this.notifyError(exc);
  1197.         }
  1198.     },
  1199.     
  1200.     // called before the delete actually takes place
  1201.     onCalendarDeleting: function calWcapSession_onCalendarDeleting(cal)
  1202.     {
  1203.     },
  1204.     
  1205.     // called after the pref is set
  1206.     onCalendarPrefChanged: function calWcapSession_onCalendarPrefChanged(cal, name, value, oldvalue)
  1207.     {
  1208.     },
  1209.     
  1210.     // called before the pref is deleted
  1211.     onCalendarPrefDeleting: function calWcapSession_onCalendarPrefDeleting(cal, name)
  1212.     {
  1213.     }
  1214. };
  1215.  
  1216. var g_confirmedHttpLogins = null;
  1217. function confirmInsecureLogin(uri)
  1218. {
  1219.     if (!g_confirmedHttpLogins) {
  1220.         g_confirmedHttpLogins = {};
  1221.         var confirmedHttpLogins = getPref(
  1222.             "calendar.wcap.confirmed_http_logins", "");
  1223.         var tuples = confirmedHttpLogins.split(',');
  1224.         for each (var tuple in tuples) {
  1225.             var ar = tuple.split(':');
  1226.             g_confirmedHttpLogins[ar[0]] = ar[1];
  1227.         }
  1228.     }
  1229.     
  1230.     var bConfirmed = false;
  1231.     
  1232.     var host = uri.hostPort;
  1233.     var encodedHost = encodeURIComponent(host);
  1234.     var confirmedEntry = g_confirmedHttpLogins[encodedHost];
  1235.     if (confirmedEntry) {
  1236.         bConfirmed = (confirmedEntry == "1");
  1237.     }
  1238.     else {
  1239.         var prompt = getWindowWatcher().getNewPrompter(null);
  1240.         var out_dontAskAgain = { value: false };
  1241.         var bConfirmed = prompt.confirmCheck(
  1242.             calGetString("wcap", "noHttpsConfirmation.label"),
  1243.             calGetString("wcap", "noHttpsConfirmation.text", [host]),
  1244.             calGetString("wcap", "noHttpsConfirmation.check.text"),
  1245.             out_dontAskAgain);
  1246.  
  1247.         if (out_dontAskAgain.value) {
  1248.             // save decision for all running calendars and
  1249.             // all future confirmations:
  1250.             var confirmedHttpLogins = getPref("calendar.wcap.confirmed_http_logins", "");
  1251.             if (confirmedHttpLogins.length > 0)
  1252.                 confirmedHttpLogins += ",";
  1253.             confirmedEntry = (bConfirmed ? "1" : "0");
  1254.             confirmedHttpLogins += (encodedHost + ":" + confirmedEntry);
  1255.             setPref("calendar.wcap.confirmed_http_logins", "CHAR", confirmedHttpLogins);
  1256.             getPref("calendar.wcap.confirmed_http_logins"); // log written entry
  1257.             g_confirmedHttpLogins[encodedHost] = confirmedEntry;
  1258.         }
  1259.     }
  1260.  
  1261.     log("returned: " + bConfirmed, "confirmInsecureLogin(" + host + ")");
  1262.     return bConfirmed;
  1263. }
  1264.  
  1265.